﻿//
// Created by Administrator on 2022/1/18.
//

//#include <wx/msw/private.h>
//#include <wx/msw/listctrl.h>
//#include <wx/listbase.h>
#include "MyDialog.h"
#include "ids.h"
#include "action_event.h"
#include "windows.h"
#include "AddDirectoryDialog.h"
#include "ReadDirectoryChanges.h"
#include "../../board/src/MyDialog.h"


BEGIN_EVENT_TABLE ( MyDialog, wxFrame )
                EVT_BUTTON ( ID_BUTTON_ADD_DIRECTORY, MyDialog::AddDirectory )
                EVT_BUTTON ( ID_REMOVE_ITEM, MyDialog::RemoveDir )
                //通过消息，传导添加项目
                EVT_MENU ( ACTION_EVENT, MyDialog::OnActionEvent )
END_EVENT_TABLE()

int watchIt( wxString path );

static void LOG_INFO(const wchar_t *string, const wchar_t *str, DWORD err);

class PostFileWorker: public wxThread
{
    //friend class MainDialog;
    MyDialog* dialog;

public:
    PostFileWorker ( MyDialog* dialog ){
        this->dialog = dialog;
    }

    //how to get the notification
    void * Entry() override
    {
        //watchIt( dialog->getMonitorPath() );
        CReadDirectoryChanges* changes = dialog->getChanges();
        while (!bTerminate)
        {
            //有信息就返回
            //DWORD rc = ::WaitForMultipleObjectsEx(_countof(handles), handles, false, INFINITE, true);
            DWORD rc = WaitForSingleObject(changes.GetWaitHandle(), INFINITE);
            switch (rc)
            {
                case WAIT_OBJECT_0 + 0:
                    // hStdIn was signaled. This can happen due to mouse input, focus change,
                    // Shift keys, and more.  Delegate to TryGetKeyboardInput().
                    // TryGetKeyboardInput sets bTerminate to true if the user hits Esc.
                    /*
                    if (TryGetKeyboardInput(hStdIn, bTerminate, buf, MAX_PATH))
                    {
                        changes.AddDirectory(CStringW(buf), false, dwNotificationFlags, dwBufSize);
                    }*/
                    //break;
                //case WAIT_OBJECT_0 + 1:
                    // We've received a notification in the queue.
                {
                    DWORD dwAction;
                    wxString strFilename;
                    if (changes.CheckOverflow())
                    {
                        wprintf(L"Queue overflowed.\n");
                    }
                    else
                    {
                        changes.Pop(dwAction, strFilename);
                        wprintf(L"%s %s\n", ExplainAction(dwAction), strFilename.GetBuffer());
                    }
                }
                    break;
                case WAIT_IO_COMPLETION:
                    // Nothing to do.
                    break;
            }
        }

        // Just for sample purposes. The destructor will
        // call Terminate() automatically.
        changes.Terminate();
        return EXIT_SUCCESS;
        //return 0;
    }
};

//get notification from thread
void MyDialog::OnActionEvent ( wxCommandEvent &event )
{
    const int token = event.GetInt();
    const long liType = event.GetExtraLong();
    switch ( token ) {
        case TOKEN_MONITOR_DIR_OVERFLOW:
            break;
        case TOKEN_MONITOR_DIR_ACTION:
            break;
    }
}

/*
static int watchIt( wxString path ){
    HANDLE m_hDirectory = CreateFile( path.wc_str(),
        GENERIC_READ | GENERIC_WRITE | FILE_LIST_DIRECTORY ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS  | FILE_FLAG_OVERLAPPED,
        NULL);
    if(m_hDirectory==INVALID_HANDLE_VALUE)
    {
        DWORD dwErr=GetLastError();
        return -1;
    }
    char notify[1024];
    memset(notify, 0, sizeof(notify));
    auto *pNotification=(FILE_NOTIFY_INFORMATION *)notify;
    DWORD BytesReturned=0;
    BOOL watch_state;
    while (TRUE)
    {
        ZeroMemory(pNotification, sizeof(notify));
        //how to setup the completion routine
        //overlapped
        watch_state = ReadDirectoryChangesW (m_hDirectory,
                                          &notify,
                                          sizeof(notify),
                                          TRUE,   //监控子目录
                                          FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_LAST_WRITE , //FILE_NOTIFY_CHANGE_DIR_NAME FILE_NOTIFY_CHANGE_CREATION FILE_NOTIFY_CHANGE_SIZE
                                          (LPDWORD)&BytesReturned,
                                          nullptr,
                                          nullptr);

        if (GetLastError()==ERROR_INVALID_FUNCTION)
        {
            //LOG_INFO(_T("文件监控，系统不支持! %s"), path.wc_str(), 0 );
            break;
        }
        else if(watch_state == FALSE)
        {
            DWORD dwErr = GetLastError();
            //LOG_INFO(_T("文件监控，监控失败! %s (LastError: %d)"), path.wc_str(), dwErr);
            break;
        }
        else if (GetLastError()==ERROR_NOTIFY_ENUM_DIR)
        {
            //LOG_INFO(_T("文件监控，内存溢出! %s"), path.wc_str(), 0 );
            continue;
        }
        else
        {
            //这里主要就是检测返回的信息，(FILE_NOTIFY_INFORMATION)
            CString szFileName(pNotification->FileName, pNotification->FileNameLength / sizeof(wchar_t));

            if (pNotification->Action==FILE_ACTION_ADDED)
            {
                //LOG_INFO(_T("文件监控，新增文件! %s\\%s"), path.wc_str(), szFileName);
            }
            else if (pNotification->Action==FILE_ACTION_REMOVED)
            {
                //LOG_INFO(_T("文件监控，删除文件! %s\\%s"), path.wc_str(), szFileName);
            }
            else if (pNotification->Action==FILE_ACTION_MODIFIED)
            {
                //LOG_INFO(_T("文件监控，修改文件! %s\\%s"), path.wc_str(), szFileName);
            }
            else if (pNotification->Action==FILE_ACTION_RENAMED_OLD_NAME)
            {
                //LOG_INFO(_T("文件监控，重命名文件! %s\\%s"), path.wc_str(), szFileName);
            }
            else if (pNotification->Action==FILE_ACTION_RENAMED_NEW_NAME) //还没出现过这种情况
            {
                //LOG_INFO(_T("文件监控，重命名文件2! %s\\%s"), path.wc_str(), szFileName);
            }

            //PostMessage通知主线程
        }
    }

    CloseHandle(m_hDirectory);
    return 0;
}
*/
static void LOG_INFO(const wchar_t *string, const wchar_t *str, DWORD err = 0 ) {

}

class DirPanel:public wxPanel{
public:
    /*
     * https://forums.wxwidgets.org/viewtopic.php?t=48800
     * A sizer can't be assigned to a window panel_top->SetSizer (sizer_frame_upper);
     * and inserted into another sizer sizer_frame->Add (sizer_frame_upper, 0, wxEXPAND); at the same time.

        If panel_top is destroyed, it will destroy the sizer.
        If sizer_frame is destroyed, it will also destroy the sizer.
     */
    DirPanel( wxWindow *parent, MonitorDIR _dir ): wxPanel( parent ){
        this->dir = _dir;
        auto *box = new wxBoxSizer(wxHORIZONTAL );

        auto *l = new wxTextCtrl( this,wxID_ANY, "dir.path" );
        box->Add( l );
        //dir name
        auto * d = new wxCheckBox( this, wxID_ANY, wxT("d"));
        d->SetValue(dir.dir_name);
        d->Enable(false);
        box->Add( d );
        //file name
        auto *f = new wxCheckBox( this, wxID_ANY, wxT("f"));
        f->SetValue(dir.file_name);
        f->Enable(false);
        box->Add( f );
        //create
        auto * c = new wxCheckBox( this, wxID_ANY, wxT("c"));
        c->SetValue(dir.create);
        c->Enable(false);
        box->Add( c );
        //write
        auto * w = new wxCheckBox( this, wxID_ANY, wxT("w"));
        w->SetValue(dir.last_write);
        w->Enable(false);
        box->Add( w );
        //subTree
        auto * r = new wxCheckBox( this, wxID_ANY, wxT("r"));
        r->SetValue(dir.subTree);
        r->Enable(false);
        box->Add( r);
        //delete the item
        //wxBitmap bitmap(wxT("delete.bmp"));
        //auto * b = new wxBitmapButton( parent, ID_REMOVE_ITEM, bitmap );
        //box->Add( b );
        this->SetSizer( box );
        this->Layout();
        box->Fit( this );
    }
private:
    MonitorDIR dir;
};

/*
 * include "action_event.h"
 * create the event callback setup
 * base layout in MyDialog
 *  dialogRootBoxSizer
 *      add m_listbox
 *
 * 公用ID
 */
void MyDialog::AddDirectory( wxCommandEvent &event ) {
    wxString path = m_filePicker1->GetPath();
    if( path.IsSameAs(""))
        return;
    //
    int i;
    //检测是否是当前项的子目录
    for (i=0; i< dirs.size(); i++){
        if( dirs.at(i).path.IsSameAs( path ) )
            return;
    }
    //show model dialog
    auto * dialog = new AddDirectoryDialog( this, this->m_filePicker1->GetPath() );
    dialog->ShowModal();
    if( dialog->ok ){
        //auto *text = new wxTextCtrl( this, wxID_ANY, this->m_filePicker1->GetPath() );
        //show the MonitorDir panel
        /*
         * before:
         *  container
         *
         * root:
         *  BoxSizer:               panelRootBoxSizer
         *      wxScrolledWindow    m_listbox
         *          Panel
         *          Panel
         *  SetSizer
         *
         * Panel:
         *  BoxSizer:       Panel root size
         *      add controls
         *  SetSizer
         * */


        /*
         * after:
         * wxPanel *m_panel = new ListItemPanel( this, _iterator.operator*() );
            panelRootBoxSizer->Add(m_panel, 0,
                                   wxEXPAND|wxALL, 15);
            panels.push_back(m_panel);
            m_listbox->SetSizer( panelRootBoxSizer );
         */
        //
        //dialogRootBoxSizer->Add(m_listbox, 0, wxALL|wxEXPAND, 0 );
        MonitorDIR dir = dialog->getMonitorDir();
        putDir( dir );
        //耦合

        //m_listbox->Add( new DirPanel( this, dir ),
        //        0, wxALL | wxEXPAND, 5 );
        //dialogRootBoxSizer->Fit( this );
        dialogRootBoxSizer->Layout();
        m_filePicker1->SetPath("");
        //show
        dirs.push_back( dir );
        //add to work thread

    }
}

/*
 * 开始监控
 * 添加
 * show
 * 常用需要监控的目录配置写入配置文件
 * dialog sizer
 * panel sizer
 *
 */
MyDialog::MyDialog(wxWindow *parent):wxFrame( parent, wxID_ANY, "abc" ) {
    //SetTitle("abc");
    //setStatusBar
    //setToolBar
    SetClientSize ( wxSize ( 350, 500 ) );
    dialogRootBoxSizer = new wxBoxSizer ( wxVERTICAL );
    m_filePicker1 = new wxDirPickerCtrl ( this, ID_FILE_PICKER, wxEmptyString,
            wxT ( "Select a DIR" ),
            wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE );
    dialogRootBoxSizer->Add ( m_filePicker1, 0, wxALL | wxEXPAND, 5 );

    button = new wxButton ( this, ID_BUTTON_ADD_DIRECTORY, "选择目录" );
    dialogRootBoxSizer->Add ( button, 0, wxALL | wxEXPAND, 5 );
    //
    //panelRootBoxSizer = new wxBoxSizer(wxVERTICAL);

    m_listbox = new wxScrolledWindow(this, wxID_ANY,
                                     wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL );
    m_listbox->SetScrollRate(5, 5 );

    wScrollSizer10 = new wxBoxSizer(wxVERTICAL );
    //
    //m_listbox = new wxBoxSizer( wxVERTICAL );
    //m_listbox: panelRootBoxSizer
    //panels: innerSizer
    //eg: put something into the list box
    //m_listbox->SetScrollRate(5, 5 );
    //
    /*
    listCtrlReport = new wxListCtrl(
            this, wxID_ANY, wxDefaultPosition, wxSize(400, 400),
            wxLC_REPORT|wxLC_SINGLE_SEL );;
            dialogRootBoxSizer->Add( listCtrlReport, wxSizerFlags(2).Expand().Border()));
    dialogRootBoxSizer->Add( listCtrlReport );
    dialogRootBoxSizer->Add( listCtrlReport, 0, wxALL|wxEXPAND, 0 );
    */
    //put a sizer adapter with the
    //demo(wScrollSizer10);

    //////////////////
    m_listbox->SetSizer( wScrollSizer10 );
    m_listbox->Layout();
    wScrollSizer10->Fit( m_listbox );
    //dialogRootBoxSizer->Add( m_listbox, 1, wxEXPAND | wxALL, 5 );
    //////////////////
    dialogRootBoxSizer->Add( m_listbox, 1, wxALL|wxEXPAND, 0 );
    this->SetSizer( dialogRootBoxSizer );
    this->Layout();
    this->Centre( wxBOTH );
    bTerminate = false;
}

MyDialog::~MyDialog() {
    //wxVector<*>
}

wxString MyDialog::getMonitorPath() {
    return wxString();
}

//remove it from list and view
void MyDialog::RemoveDir(wxCommandEvent &event) {
    event.GetClientData();
}

//eg:
void MyDialog::demo(wxBoxSizer* wScrollSizer10 ) {
    auto* m_panel8 = new wxPanel( m_listbox, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
    //
    wxBoxSizer* bSizer11;
    bSizer11 = new wxBoxSizer( wxHORIZONTAL );

    auto *m_button2 = new wxButton( m_panel8, wxID_ANY, wxT("MyButton"), wxDefaultPosition, wxDefaultSize, 0 );
    bSizer11->Add( m_button2, 0, wxALL, 5 );

    auto* m_toggleBtn1 = new wxToggleButton( m_panel8,
            wxID_ANY, wxT("Toggle me!"), wxDefaultPosition, wxDefaultSize, 0 );
    bSizer11->Add( m_toggleBtn1, 0, wxALL, 5 );
    //

    m_panel8->SetSizer( bSizer11 );
    m_panel8->Layout();
    bSizer11->Fit( m_panel8 );
    //
    wScrollSizer10->Add( m_panel8, 1, wxEXPAND | wxALL, 5 );
}

void MyDialog::putDir( MonitorDIR dir ) {

    auto* m_panel8 = new DirPanel( m_listbox, dir );
    //wxBoxSizer* bSizer11;
    //bSizer11 = new wxBoxSizer( wxHORIZONTAL );
    ///////////////.........

    //////////////
   //m_panel8->SetSizer( bSizer11 );
    //m_panel8->Layout();
    //bSizer11->Fit( m_panel8 );
    //
    wScrollSizer10->Add( m_panel8, 0, wxEXPAND | wxALL, 5 );
    //m_listbox->Layout();
    //wScrollSizer10->Fit( m_listbox );
    //add directory
    if( dirs.size()==0 ){
        //start the thread

    }
}


int MyDialog::init(){
    using namespace rdc;

    EIoModelType eIoModel = eIoModel_CompletionRoutine;
    DWORD dwMaxChanges = 4096;
    /*
    const DWORD dwNotificationFlags = FILE_NOTIFY_CHANGE_LAST_WRITE
                                      | FILE_NOTIFY_CHANGE_CREATION
                                      | FILE_NOTIFY_CHANGE_FILE_NAME
                                      | FILE_NOTIFY_CHANGE_DIR_NAME;
    */
    // Create the monitor and add two directories.
    changes = new CReadDirectoryChanges(eIoModel, dwMaxChanges);

    //const HANDLE handles[] = { hStdIn, changes.GetWaitHandle() };
    //changes.GetWaitHandle();
    //wait
}

int MyDialog::backgroundRun(){

}

//LPCWSTR ExplainAction(DWORD dwAction);
//bool TryGetKeyboardInput(HANDLE hStdIn, bool &bTerminate, char* buf, int len);

//
// When the application starts, it immediately starts monitoring your home
// directory, including children, as well as C:\, not including children.
// The application exits when you hit Esc.
// You can add a directory to the monitoring list by typing the directory
// name and hitting Enter. Notifications will pause while you type.
//

int MyDialog::addDirectory( wxString dir, DWORD dwNotificationFlags )
{
    DWORD dwBufSize = 16 * 1024;
    //changes.AddDirectory(_tgetenv(_T("USERPROFILE")), true, dwNotificationFlags);
    changes.AddDirectory( dir.wc_str(), true, dwNotificationFlags, dwBufSize );

    //HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
    //
    //char buf[MAX_PATH] = { 0 };

}

CReadDirectoryChanges* MyDialog::getChanges(){
    return changes;
}